Rhapsody Developer Release. Copyright 1997 by
Apple Computer, Inc. All Rights Reserved.
The Foundation Framework is a library of Objective-C classes that provide the infrastructure for object-based applications without graphical user interfaces as well as for most other Yellow Box frameworks, most notably the Application Kit. Foundation consists of basic classes used by any Objective-C program:
You can find several Foundation examples in /NextDeveloper/Examples/Foundation. Various examples illustrating the use of Distributed OLE and the NeXT ORB can be found on NeXTanswers. NeXTanswers can be accessed through http://enterprise.apple.com.
These release notes are divided into two major sections:
The Foundation Topics section covers the following subjects:
The release note Implications of the Year 2000 for Rhapsody also contains Foundation-related information of interest to developers.
In the Developer release of Rhapsody, the primary way to move files, rename files, access file attributes, and perform other file operations remains the NSFileManager class. The Rhapsody Premier release will offer a new set of APIs for this type of architecture. All development on the NSFileManager class will then stop (consequently NSFileManager might not work with HFS file systems). However, the class itself will probably remain for compatibility reasons. You should keep this scheduled obsolescence in mind if you use NSFileManager in new programs.
The NSObject instance methods performSelector:
,
performSelector:withObject:
, and
performSelector:withObject:withObject:
have a return
value of id
. This is a vestige of pre-OpenStep
conventions where methods without otherwise interesting return values
returned self
.
Much of the time one can
"get away with" performing a selector on an object with these
methods, where the target method does not have a return value or has
a non-object return value. Such use is semantically suspect, however,
and is not valid.
Any situation where the performSelector:
message (and
variants)
is forwarded illustrates this (with an
abnormal program termination); the NSInvocation object attempts to
retain the return value from after invocation because the selector
for performSelector:
is typed to return an object.
Sending performSelector:
to a faulted object with a
selector that does not return id
is an example of this.
This behavior extends also to various other "perform" methods such as
the makeObjectsPerformSelector:
of some collection
classes and the "delayed perform" and "ordered perform" methods in
NSRunLoop.h.
The NSLog()
and NSLogv()
functions do
not log an error message to stderr, as stated in the
documentation.
On HP-UX, Solaris, and Mach systems (such as Rhapsody), these functions write the log to STDERR_FILENO if the file descriptor is open. If that fails, the function sends the message to the syslog subsystem with the LOG_USER facility (or default facility if LOG_USER doesn't exist on a platform) and with priority LOG_ERR (or similar, depending on what the platform supports). If both of these attempts to write the message fail, the message is discarded.
On Windows platforms, the message is written to the
STD_ERROR_HANDLE, if that standard handle is valid and supported on
the system. It is also written to the Windows Event Log on Windows
platforms that support the Event Log; otherwise it is written to the
file c:\fndation.log (if that file can be opened). If all of
these attempts fail, the message is discarded. On some Windows
platforms, the message to the Event Log may be truncated if there is
a limit to the size of a message that the Event Log can accept. Note
that this implies that the Event Log can fill up with
NSLog()
-generated messages, thus causing application
crashes. You should be aware of this and make the appropriate
settings in the Event Log application to discard old messages.
On Windows platforms that support an application discovering
whether or not it is running under a debugger, NSLog()
and NSLogv()
may only send the message to the debugger
for its handling, via standard WIN32 mechanisms, and not also write
the message to STD_ERROR_HANDLE and the Event Log. Note that a
debugger may choose not to display messages thus sent to it,
or may choose not to display all messages; NSLog()
and
NSLogv()
have no control over that.
Output from NSLog()
and NSLogv()
is
serialized, in that only one thread in a process can be doing the
logging described above at a time. All attempts at logging a message
complete before the next thread can begin its attempts.
The effects of NSLog()
and NSLogv()
are
not serialized with subsystems other than those discussed above (such
as the standard I/O package) and do not produce side effects on those
subsystems (such as causing buffered output to be flushed, which may
be undesirable).
The format specification allowed by NSLog()
and
NSLogv()
is that which is understood by NSString's
formatting capabilities. That is not necessarily the set of format
escapes and flags understood by the printf()
function,
as mentioned in the documentation.
NSUnarchivers own the objects that they decode. When an NSUnarchiver is deallocated, so are the objects that it has decoded, unless they have been retained. The following code, for example, will probably result in a decoded object being used after being freed:
NSAutoreleasePool *pool = [[NSAutoreleasePool allocWithZone:NULL] init]; NSUnarchiver *unarchiver = [[NSUnarchiver allocWithZone:NULL] initForReadingWithData:myData ]; id object = [unarchiver decodeObject]; [unarchiver release]; [pool release]; .. use or return object here ...
The object object
should be retained before the
NSUnarchiver is released, and probably should also be autoreleased if
object
is returned from the function or method.
Exceptions to this are the instance methods
decodeValueOfObjCType:at:
and
decodeValueOfObjCTypes:
(and variants).
Objects "returned" from these two methods, like those returned from
NSObject's allocWithZone:
and
copyWithZone:
, are not autoreleased and must be
explicitly released.
When a method like NSDictionary's
dictionaryWithContentsOfFile:
is used to read and parse
an ASCII property list from a file, the display of parse errors and
exceptions is suppressed, and nil
is returned upon
error. However, you might want to know about syntax errors in a
property list, or you might want finer-grained information about why
a property list cannot be read and parsed. You can use another
method, NSString's propertyList
, for this purpose. The
following example illustrates this technique:
- (id)readPropertyListFromFile:(NSString *)path mustBeOfType:(Class)targetClass logErrors:(BOOL)showErrors { NSString *string; id plist = nil; string = [[NSString allocWithZone:NULL] initWithContentsOfFile:path]; if (showErrors && nil == string) { NSLog(@"%@: string could not be read from '%@'", NSStringFromSelector(_cmd), path); } NS_DURING plist = [string propertyList]; NS_HANDLER if (showErrors) { NSLog(@"%@: received exception while parsing: %@", NSStringFromSelector(_cmd), localException); } plist = nil; NS_ENDHANDLER [string release]; if (nil != targetClass && ![plist isKindOfClass:targetClass]) { if (showErrors) { NSLog(@"%@: property list is not of desired type '%@'. It's an '%@'.", NSStringFromSelector(_cmd), NSStringFromClass(targetClass), NSStringFromClass([plist class])); } [plist release]; plist = nil; } return plist; }
Grammars and description of the serialization (NSSerializer) and ASCII property list representation formats are available in the appendix to the OpenStep specification. Strings containing non-alphanumeric characters must be double-quoted in property lists to ensure the parsing of the entire string as one string object; otherwise, parse failures will result.
The retain/release strategy used in Foundation allows an incautious programmer to create reference cycles in an object graph (that is, the graph of objects that retain one another in some manner). These cycles are self-sustaining and constitute a memory leak. The simplest example is the creation of an NSMutableArray instance that is then added to itself. Since an array retains its objects, it retains itself independent of its normal usage. The effects of retaining are essentially distributed across the network via the Distributed Objects mechanism.
If you cannot avoid retain cycles by careful design, but know about their existence, you can use the following technique to recover the memory. For the objects in a retain cycle whose retain counts are solely due to the cycle, you should retain all objects (perhaps in an array), then tell each one to release its retained objects, then release all objects. The action of releasing its retained objects would undoubtedly cause an object to become dysfunctional. Some objects already implement such a method by the name "invalidate". It is a bug that neither NSObject nor the collections implement this method to facilitate cycle recovery.
The following statements are false:
Returned objects are guaranteed to be valid for the scope of the current method.
Returned objects are guaranteed to be valid until the current autorelease pool is released.
Returned objects are guaranteed to be valid until the end of the current event loop.
The Foundation's retain count mechanism operates through the
NSObject methods retain
and release
. A code
fragment such as:
id object = [collection returnObject];
creates a reference (in object
) to the returned
object, but does not increment the retain count of
object
. If you don't let the system know about your
reference, by incrementing the retain count with retain
,
the system can't ensure that your reference remains valid for any
length of time. For example:
object = [array objectAtIndex:7]; /* get object */ [array removeObjectAtIndex:7]; /* removes object and releases it */ [object dosomething ]; /* object may be invalid here */
In this example, if array
had the last retain on
object
, the second line will cause object
to be deallocated and the third line will cause the application to
crash. Here is another example with the Application Kit:
id oldTitle = [myWindow title]; /* get current title */ [myWindow setTitle:@"Calculating..."]; /* set temporary title */ /*... do calculation here ... */ [myWindow setTitle:oldTitle]; /* restore previous title */
Here, the setTitle:
method gets rid of its reference
to the existing title string and takes a reference to the new title
string. If the window had the only reference to the previous title
string object, the last line of the example will attempt to use
setTitle:
with a freed object. If the Application Kit in
its implementation happens to send autorelease
to the
old title instead of release
, then the object referenced
in oldTitle
might not be freed by the last line, but in
general you cannot know whether such a method will
release
or autorelease
the previous object.
The Foundation almost exclusively uses release
, since
that does not have the performance overhead of autoreleasing, and
Foundation objects are very common in applications.
In practice, you can get away with such "weak references" much of the time. But the safest approach formalizes your reference to the object:
id object = [[collection returnObject] retain]; /*... do operations, some of which may be on "object" ... */ /* Don't need object any longer */ [object release]; object = nil;
An obscure requirement documented in the NSObject class
specification for the hash
method deserves elaboration.
The hash
and isEqual:
methods of a class
must be defined so that if two objects are equal, their hash value is
the same. Thus
[x isEqual:y]
implies
[x hash] == [y hash]
However, the converse is not true. This is easy to forget, but especially important to keep in mind when putting custom objects into some collections (such as NSSet and NSDictionary objects).
Objects wishing to forward messages with the
forwardInvocation:
method must also override the
methodSignatureForSelector:
method. This requirement
results from the compiler not providing call-stack descriptive
information at each call site. Consequently, the run time needs a
source of information for how the stack frame is constructed. It
needs this information to build the invocation that will be supplied
to the forwardInvocation:
method call. The
methodSignatureForSelector:
method must be reimplemented
to provide a method signature for selectors to which the object does
not respond.
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { NSMethodSignature *result = [super methodSignatureForSelector:sel]; // if result is nil, self must not respond to the selector if (nil == result) result = [target methodSignatureForSelector:sel]; return result; } - (void)forwardInvocation:(NSInvocation *)invocation { [invocation invokeWithTarget:target]; // let target implement methods that we do not }
Faulted objects in EOF that wish to forward messages should also
override the class method
instanceMethodSignatureForSelector:
. See the
ForwardInvocation example in
/NextDeveloper/Examples/Foundation/ForwardInvocation.
The header file NSDebug.h has some global flags, functions, and methods that you may find useful in debugging. But be advised: Although this header file is public, its contents are mostly unsupported, largely undocumented, and subject to change without warning. Do not depend on APIs within this header for the operation of production programs.
One of the useful flags defined in NSDebug.h is
NSKeepAllocationStatistics
, which allows you to analyze
the object allocation activity in a program. You can use the
ObjectAlloc demo application that comes with Rhapsody and Yellow Box
for Windows to graph the object allocation activity in real time.
The load
message is sent to classes when they are
added to the Objective-C run time. Defined as a class method in
NSObject, load
is usually sent before
initialize
. Subclasses of NSObject do not inherit
load
.The order in which load
messages are
sent to classes is unspecified (and specifically, a class's
superclass is not guaranteed to have its load
method
called before the class receives the message). Therefore, you should
not use any subsystems or classes that are loaded at the same time if
you do not know they have received the load
message. If
you do, you will probably cause execution of code that assumes that
load
(and anything that had to be done in
load
) has been previously invoked.
Typically, classes get added to the run time in one of two ways:
when loaded dynamically from a bundle or framework, or by NSBundle at
application launch. The restrictions on load
are perhaps
most serious at launch time. The Foundation has classes and
subsystems (as might any other library) that depend on
load
being invoked before they are fully functional. For
example, you should not use NSZoneMalloc()
to allocate
memory in load
methods in objects that are statically
linked into an application. You certainly should not create any
objects or do anything that creates objects.
Note that sending load
to a class isn't a solution
(and is a generally bad idea), because that will cause
initialize
to be sent first, which the class may not
handle. Moreover, sending load
places into your code
dependencies on which classes implement load
in a
particular version of a library. In the case of Foundation, for
convenience sake, load
methods that must use Foundation
API can call send self
to NSObject first to make sure
Foundation has performed the initialization it needs.
The load
method is not intended as a general
initialization mechanism and should be used only when absolutely
necessary. Use initialize
for early initialization
whenever possible.
NSNotificationCenter, although heavily optimized, can become a performance bottleneck if application code makes heavy use of notifications. Use the notification mechanism judiciously.
You should never use the NSAutoreleasePool instance method
addObject:
to autorelease an object. Instead use the
autorelease
method or the class method
addObject:
. Using the instance method incurs the risk of
adding an object to a pool that is not the top autorelease pool, a
semantically suspect operation, since the autorelease-pool model is
one of a (per-thread) stack of pools. Rhapsody does allow you to add
objects to an autorelease pool that is not the top pool, but it is a
much more expensive operation than adding an object to the top pool.
Frameworks are a specialized form of bundle. An application can statically link against a framework at compile time (like a library), or dynamically load a framework (like a bundle) at run time. However, as a specialized type of bundle, there are some constraints placed on frameworks:
This section documents many of the known problems with Foundation in Rhapsody Developer. Not all of the problems apply to the current release; refer to the Releases and Disposition fields for each problem. Defects not present in the current release are of interest to those developing software on the current release with plans to deploy on a previous release. (However, we do not recommend that developers develop on a version newer than that planned for deployment.)
Reference |
47347 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
NSArchiver and NSUnarchiver do not understand bitfields. |
Description |
Structures containing bitfields cannot be archived and unarchived. |
Workaround |
Encode the members of a structure containing bitfields
individually, encoding the bitfields as |
Reference |
49084 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
NSInvocation always retains the return value. |
Description |
NSInvocation always retains object return values. This
can cause problems if the return value of a method is not a
fully initialized object. Only the NSObject class methods
|
Workaround |
None |
Reference |
51108 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
Kanji text in |
Description |
Strings that contain non-ASCII characters are displayed with the non-ASCII characters translated to a hex representation (\uNNNN). This makes log strings with Kanji essentially unreadable. |
Workaround |
None |
Reference |
56644 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
NSArchiver tracks |
Description |
During archiving, NSArchiver keeps track of the
|
Workaround |
Do not archive |
Reference |
56659 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
Proxies cannot be notification observers unless they are retained. |
Description |
Because NSNotificationCenter does not retain observers, registering an observer with a remote NSNotificationCenter (the observer would be a proxy in the remote process) will eventually cause a crash of the remote process when it tries to send a message to a freed object, unless the remote process explicitly retains the proxy. This situation is a bit arcane, but the same type of problem can happen with other objects that don't retain objects that they know about. |
Workaround |
Use an explicit protocol to tell the server (the process vending the notification center) to register and unregister an observer. The server then retains and registers the observer with the vended notification center, and unregisters and releases the observer when the client tells it to do so. (If an unregister mechanism is not part of the protocol, and used, the object will never be released.) |
Reference |
57984 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
Can't get a list of command-line arguments that are not defaults. |
Description |
There is no way to get a list of the process arguments that NSUserDefaults did not interpret as default vaules. |
Workaround |
The NSArgumentDomain dictionary can be retrieved from the standard user defaults instance to find out what arguments were interpreted as default options. |
Reference |
58137 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
Can't create an NSData object that does not free the bytes. |
Description |
NSData and NSMutableData instances always take ownership
of the byte pointer with which they are initialized, and
then call |
Workaround |
None. |
Reference |
62235 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
Can't search for characters in a case-insensitive mode. |
Description |
NSString's |
Workaround |
None. |
Reference |
62576 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
NSNumber's |
Description |
Two NSNumbers containing NaN are compared and seem to be
equal; for example, they are created with
|
Workaround |
None. |
Reference |
62812 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
Changes to defaults sometimes appear not to take effect. |
Description |
The default search order for domains in NSUserDefaults
places the NSArgumentDomain before the application's domain.
This is usually desirable for reading defaults, but may not
be what you want if you set or otherwise change default
values. If you set a value for a default that was passed as
an option on the command-line, the value is correctly set in
the application's domain, but |
Workaround |
The available workarounds depend upon the application and its use of user defaults. The following options pertain to a specific NSUserDefaults instance; if you create multiple instances of NSUserDefaults, you'd have to do the workaround for each individually.
|
Reference |
64605 |
Releases |
Rhapsody Developer |
Platforms |
HP-UX |
Disposition |
None |
Problem |
NSBundles cannot dynamically load code |
Description |
Unlike Apple's implementations of PDO on other platforms, Foundation in PDO for HP-UX cannot dynamically load code. This is due to limitations in the compiler and Objective-C runtime, which will be resolved in a future release. |
Workaround |
None |
Reference |
66538 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
NSString's |
Description |
Unlike what the documentation seems to say, NSString does
not understand all ANSI C |
Workaround |
Use |
Reference |
66766 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
NSBundle doesn't load |
Description |
NSBundles do not attempt to load code from debug or
profile versions of their binary code, to which the
makefiles append the |
Workaround |
Mach, Solaris, HP-UX: Create a symbolic link named
without the |
Reference |
68155 |
Releases |
Rhapsody Developer |
Platforms |
Windows |
Disposition |
None |
Problem |
NSUserDefaults does not reflect Windows Registry case semantics. |
Description |
The Windows Registry is case-preserving but case-insensitive with respect to registry keys (not default keys). NSUserDefaults doesn't reflect this in its behavior, so, for example, if a persistent domain "MyApp" exists, and a persistent domain "MYAPP" is created, the new domain will eradicate the existing one and all defaults in the existing one will be lost. This does not affect default keys or values, only domain names. |
Workaround |
None |
Reference |
68449 |
Releases |
Rhapsody Developer |
Platforms |
Windows |
Disposition |
None |
Problem |
Cannot get a proxy for a distributed OLE object using Distributed Objects. |
Description |
When trying to get a proxy for an OLE object, you may get the following error: "deserializeObjectAt: class 'NSDistantIDispatchProxy' not loaded". |
Workaround |
Include nxorb.m (from NextDeveloper/Libraries ) in your client. Although the D'OLE Developer's Guide states that nxorb.m is only needed for use with NXConnections, it's needed in some circumstances for NSConnections as well. |
Reference |
68675 |
Releases |
Rhapsody Developer |
Platforms |
HP-UX |
Disposition |
None |
Problem |
Multi-threaded Distributed Objects example occasionally hangs on HP-UX. |
Description |
If you compile the multithreaded Distributed Objects example in /NextDeveloper/Examples/Foundation/MultiThreadedDO and then run it, the program will occasionally hang. |
Workaround |
None |
Reference |
69110 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
( |
Description |
This is actually correct behavior, in that NSNumber
follows the C type promotion rules. Thus, the ( |
Workaround |
None |
Reference |
69152 |
Releases |
Rhapsody Developer |
Platforms |
HP-UX |
Disposition |
None |
Problem |
The HP-UX nmserver has problems passing large structures in the absence of a protocol. |
Description |
On HP-UX systems, passing large structures over Distributed Objects without having first defined a protocol can cause the nmserver to cease functioning. |
Workaround |
None |
Reference |
71118 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
Collections that contain themselves cannot describe themselves. |
Description |
Invoking the NSObject |
Workaround |
None |
Reference |
73320 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
In NSNumberFormatter, specifying different formats for negative and positive values causes problems. |
Description |
When the formats specified for positive and negative values aren't alike in their treatment of separators, the positive format takes on the separator characteristics of the negative format. Consider, as an example, the following statement: [numForm setFormat:@"###,##0.00;-(##0.00)"]; Here, positive values will be displayed without thousand separators even though their format includes separators. |
Workaround |
None |
Reference |
73686 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
NSFileHandle does not handle error notification correctly. |
Description |
If an error occurs when application code attempts a background monitoring operation, NSFileHandle may send an empty notification to observers. This sometimes occurs when a native file handle has not been configured correctly for use. |
Workaround |
None |
Reference |
77202 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
NSFileManager methods don't work on strict 8.3 DOS file systems. |
Description |
The NSFileManager methods
|
Workaround |
None |
Reference |
77309 |
Releases |
Rhapsody Developer |
Platforms |
All |
Disposition |
None |
Problem |
Local proxies are not immediately released when the remote proxy is released. |
Description |
For performance reasons, Distributed Objects batches, on
the remote side, release messages destined for local
proxies. Whenever a synchronous message (that is, a
non-oneway message) is sent to a remote object,
all pending releases on local objects are returned with the
return value of the method (which may be |
Workaround |
Occasionally send a synchronous message from the client to the server. The message does not have to have a return value or parameters, or do anything, but will serve to flush pended releases back to the client. Another workaround would be to occasionally invalidate the connection to the server and reconnect. |
Reference |
77559 |
Releases |
Rhapsody Developer |
Platforms |
Mach, HP-UX, Solaris |
Disposition |
None |
Problem |
Process name is empty string. |
Description |
If |
Workaround |
Pass the same string in |